#!/bin/bash
# Job yaml can send below ENVIRONMENT VARIABLES to me
# - project
# - fork
# - commit
# - tag
# - branch
# - pkgver
# - pkgrel
# - config
# - patchset
# - autoconfig
# - upstream_repo
# - pkgbuild_repo
# - warning_level
# - _url
# - _make_nr_jobs
# - _make_verbose
# - _make_extra_opts
# END ENVIRONMENT VARIABLES

# input params case1: auto benchmark depends
#   project=pxz pkgver=git pkgrel=1
#
# input params case2: auto ss depends
#   project=linux fork=linux-next tag=v6.6

# config is uploaded by end user
# autoconfig is just a name given by submit client, the content will be generated/uploaded by linux kernel

# We'll use the below global env vars, but not treat them as parameters to avoid
# duplicates in result path like /openeuler-20.03-aarch64/openeuler-aarch64-cifs-2003/
# os
# os_arch
# os_version

## makepkg is a script that automates the building of packages; it can download and validate source files,
## check dependencies, configure build-time settings, compile the sources, install into a temporary root,
## make customizations, generate meta-info, and package everything together.
## See: https://wiki.archlinux.org/index.php/Makepkg

. $LKP_SRC/lib/debug.sh
. $LKP_SRC/lib/http.sh
. $LKP_SRC/lib/upload.sh
. $LKP_SRC/lib/job.sh
. $LKP_SRC/lib/install_depends.sh

load_job_vars id os os_arch os_version rootfs remote_git_host remote_git_port git_server sched_host sched_port result_host result_port

set -o pipefail
shopt -s nullglob

###############################################################################
# Configuration
###############################################################################

: "${sched_port:=3000}"
: "${TMP:=/tmp}"

upstream_commit="$commit"
export initrd_http_prefix="http://${sched_host}:${sched_port}"
export file_store_root="/srv/file-store"

###############################################################################
# Functions
###############################################################################

check_vars()
{
	[ -n "$os" ]			|| die "os is empty"
	[ -n "$os_arch" ]		|| die "os_arch is empty"
	[ -n "$os_version" ]	|| die "os_version is empty"

	[ -n "$project$upstream_repo" ]		|| die "project/upstream_repo is empty"
	[ -n "$commit$tag$branch$pkgver" ]	|| die "upstream commit/tag/branch/pkgver is empty"
}

set_vars()
{
    if [ -n "$config" ]; then
        config_name="$config"
    elif [ -n "$autoconfig" ]; then
        config_name="$autoconfig"
    else
        config_name=defconfig
    fi

    if [ -n "$pkgver" ]; then
        package_version="$pkgver"
    elif [ -n "$commit" ]; then
        package_version="$commit"
    elif [ -n "$tag" ]; then
        package_version="$tag"
    elif [ -n "$branch" ]; then
        package_version="$branch"
    else
        die "Missing params to decide package_version"
    fi

    if [ -n "$patchset" ]; then
        patchset_name="$patchset"
    fi

    package_version=$(echo "$package_version" | sed 's/[^a-zA-Z0-9._-]/:/g')
}

# Configure build paths
# keep the path in sync with process_ss_pkgbuild()
set_remote_paths() {
    local storage_dir
    case "$project" in
        linux*|kernel*)
            storage_dir="ss/pkgbuild/$project/${os_arch}"
            export linux_CGZDEST="$CGZ_DESTDIR"
            ;;
        *)
            storage_dir="ss/pkgbuild/$project/${os}-${os_version}-${os_arch}"
            ;;
    esac

    # Ignore pkgrel since scheduler does not always know pkgrel,
    # so hard to generate a matching need_file_store path
    # So consistently ignore pkgrel for package path.
    # [ -z "$pkgrel" ] && pkgrel=$(grep '^pkgrel=' PKGBUILD | cut -d'=' -f2)

    remote_cgz_dir="${storage_dir}/${config_name}/${package_version}"
    remote_config_path="ss/config/$project/$config_name"
    remote_patchset_path="ss/patchset/$project"
}

set_upstream_vars() {
    if [ -z "$upstream_repo" ]; then
        [ -n "$project" ] || die "Project not specified"
		[ -n "$fork" ] || fork="$project"
        upstream_repo="${project:0:1}/$project/$fork"
    else
        IFS='/' read -r _ project fork <<< "$upstream_repo"
    fi
}

set_pkgbuild_repo() {
    local upstream_data
    upstream_data=$(get_upstream_data "$upstream_repo")
    [ -z "$_url" ] && _url=$(parse_yaml_value "$upstream_data" "url" | head -1)
    pkgbuild_repo=$(parse_yaml_value "$upstream_data" "pkgbuild_repo")

    # Select appropriate pkgbuild repo
    local repo
    for repo in $pkgbuild_repo; do
        if [[ "$repo" =~ (-git|linux)$ ]]; then
            pkgbuild_repo="$repo"
            break
        fi
    done
    pkgbuild_repo="pkgbuild/$pkgbuild_repo"
}

get_upstream_data() {
    local upstream_repo=$1
    if [ "$upstream_repo" = "l/linux/linux" ]; then
        cat <<EOF
url:
- https://mirrors.tuna.tsinghua.edu.cn/git/linux.git
pkgbuild_repo:
- packages//linux/trunk
- aur-l/linux
EOF
    else
        curl -sS -X POST "$remote_git_host:$remote_git_port/git_command" \
        -H 'Content-Type: application/json' \
        -d '{"git_repo": "/upstream/u/upstream-repos/upstream-repos.git", "git_command": ["git-show", "HEAD:'"$upstream_repo"'"]}' ||
        die "Failed to get upstream data"
    fi
}

parse_yaml_value() {
    local data=$1
    local key=$2
    echo "$data" | awk -v key="$key" '
        # Match the key line
        $1 == key ":" {
            # Print all subsequent lines that start with - (with or without spaces)
            while (getline > 0) {
                # Stop if we hit a non-indented line that isnt a list item
                if ($0 !~ /^[[:space:]]*- / && $0 !~ /^- /) break
                # Remove leading spaces and the - marker
                sub(/^[[:space:]]*- /, "", $0)
                print $0
            }
        }
    '
}

get_config() {
    [ -n "$config_name" ] || return
    [ "$config_name" = "defconfig" ] && return

    # Download config
    mkdir -p "$CONFIG_DESTDIR"
    export CONFIG_FILE="$CONFIG_DESTDIR/$config_name"

    # For autoconfig: randconfig, skip download - PKGBUILD will handle it completely
    # Do NOT skip for config: randconfig (to avoid affecting old data)
    if [[ -n "$autoconfig" && "$autoconfig" =~ ^randconfig ]]; then
        echo "[$(date '+%T')] Skipping download for autoconfig randconfig - will be handled in PKGBUILD"
        # Create empty file to pass the path to PKGBUILD
        touch "$CONFIG_FILE"
        return 0
    fi

    # Download normal config files (including config: randconfig)
    http_get_file "$initrd_http_prefix$file_store_root/$remote_config_path" "$CONFIG_FILE" || {
        if [ -n "$config" ]; then
            die "Cannot download config file from $initrd_http_prefix$file_store_root/$remote_config_path"
        fi
    }
}

get_patchset() {
    [ -n "$patchset_name" ] || return

    # Download patchset
    mkdir -p "$PATCHSET_DESTDIR"
    export PATCHSET_PATH="$PATCHSET_DESTDIR"
    for patch in $patchset_name; do
        http_get_file "$initrd_http_prefix$file_store_root/$remote_patchset_path/$patch" "$PATCHSET_PATH/$patch" || {
            if [ -n "$patchset" ]; then
                die "Cannot download patchset file from $initrd_http_prefix$file_store_root/$remote_patchset_path/$patch"
            fi
        }
    done
}

upload_config() {
    [ -n "$autoconfig" ] || return

    upload_one_srv "$CONFIG_DESTDIR/$config_name" "$file_store_root/$remote_config_path"
}

upload_results() {
    for file in $CGZ_DESTDIR/*
    do
        [ "${file##*/}" == "vmlinuz" ] &&  $LKP_SRC/sbin/unzip-vmlinuz $file
        test -e "$file" &&
        upload_one_srv "$file" "$file_store_root/$remote_cgz_dir/${file##*/}"
    done
}

download_pkgbuild() {
	if [ ${pkgbuild_repo%%/*} == "pkgbuild" ]; then
		request_pkgbuild_files
	else
		clone_pkgbuild_repo
	fi
}

request_pkgbuild_files() {
	local pkgrepo="${pkgbuild_repo%%//*}"
	pkgrepo="${pkgrepo}/${pkgrepo##*/}.git"

	local pkgpath=""
	[[ "$pkgrepo" =~ ^pkgbuild/(packages|community) ]] && pkgpath="${pkgpath}${pkgbuild_repo#*//}/"

	filelist=$(curl -sS -H 'Content-Type: Application/json' -XPOST "$remote_git_host:$remote_git_port"'/git_command' \
		-d '{"git_repo": "'${pkgrepo}'", "git_command": ["git-ls-tree", "--name-only", "-r", "HEAD:'${pkgpath}'"]}')

	for pkgfile in ${filelist[*]}
	do
		get_pkgfile "${pkgrepo}" "${pkgpath}${pkgfile}"
	done
}

get_pkgfile() {
	curl -sS -H 'Content-Type: Application/json' -XPOST "$remote_git_host:$remote_git_port"'/git_command' \
		-d '{"git_repo": "'$1'", "git_command": ["git-show", "HEAD:'$2'"]}' -o "${2##*/}"
}

clone_pkgbuild_repo() {
	if [[ ${pkgbuild_repo} == *git* ||  ${pkgbuild_repo} == *http* ]]; then
		local url=${pkgbuild_repo}
	else
		local url="git://${git_server}/pkgbuild/${pkgbuild_repo}"
	fi

	local proj_name=${pkgbuild_repo##*/}
	git clone -q "$url" || die "clone git repo ${proj_name} failed: $url"
    cd "${proj_name%.git}" || die "Missing pkgbuild directory"
}

prepare_pkgbuild() {
    [ -n "$pkgver" ] && sed -i "s/^pkgver=.*/pkgver=$pkgver/" PKGBUILD
    [ -n "$pkgrel" ] && sed -i "s/^pkgrel=.*/pkgrel=$pkgrel/" PKGBUILD
}

# help refine this shell code
replace_pkgbuild_source() {
    [[ -z "$_url$commit$tag$branch" ]] && return

    (
        PATH=
        # Source the original PKGBUILD to get its variables
        source PKGBUILD
        if [[ "${source[0]#*::}" != "${source[0]}" ]]; then
            repo_dir="${source%%::*}::"
        else
            repo_dir=
        fi

        # If $_url is defined, replace it into the first item of $source[]
        if [[ -n "$_url" ]]; then
            source[0]="$repo_dir$_url"
        fi

        if [[ -n "$commit" ]]; then
            source[0]="${source[0]%%#*}#commit=$commit"
        fi

        if [[ -n "$tag" ]]; then
            source[0]="${source[0]%%#*}#tag=$tag"
        fi

        if [[ -n "$branch" ]]; then
            source[0]="${source[0]%%#*}#branch=$branch"
        fi

        # Finally, write the new source array to the new source file
        echo "source=(${source[@]})" > PKGBUILD.src
    ) 2>/dev/null
}

install_makedepends() {
    $LKP_SRC/bin/map-makedepends --to-os $os@$os_version:$os_arch PKGBUILD |
        while read line
        do
            case "$line" in
                os:*)
                    packages="${line#os: }"
                    install_depends "$packages"
                    ;;
            esac
        done
}

build_package() {
    # Build make arguments from specific build variables
    local make_args=""
    [ -n "$_make_nr_jobs" ] && make_args="$make_args $_make_nr_jobs"
    [ -n "$warning_level" ] && make_args="$make_args $warning_level"
    [ -n "$_make_verbose" ] && make_args="$make_args $_make_verbose"
    [ -n "$_make_extra_opts" ] && make_args="$make_args $_make_extra_opts"

    # Export for PKGBUILD to use
    export make_args

    local makepkg_args=(
        "-A"
        "--check"
        "--skipinteg"
        "--rewritegitserver"
        "--config" "$LKP_SRC/etc/makepkg.conf"
    )

    if [ "$os" == "archlinux" ]; then
        makepkg_args+=("-s" "--needed" "--noconfirm")
        local pacman="pacman"
    else
        # In other OS, we install depends based on makedepends_debian_11 etc. fields
        install_makedepends
        makepkg_args+=("--nodeps")
        local pacman="true"
    fi

    # Execute build
    # additional vars passed to PKGBUILD:
    # $CONFIG_FILE: set if has $config_name; in $autoconfig case,
    #               PKGBUILD should create it if not exist

    # Export job-related variables so PKGBUILD can access them for uploads
    # The new scheduler uses result_host/result_port for uploads
    # If not set, use sched_host/sched_port as fallback
    if [ -z "$result_host" ] && [ -n "$sched_host" ]; then
        export result_host="$sched_host"
        export result_port="${sched_port:-3000}"
    fi

    export id
    export job_token
    export result_host
    export result_port
    export sched_host
    export sched_port

    # Ensure http_client_cmd is set and exported for PKGBUILD subshells
    # This is needed because PKGBUILD may need to upload files during prepare()
    if [ -z "$http_client_cmd" ]; then
        . $LKP_SRC/lib/curl.sh
        setup_curl
    fi
    export http_client_cmd

    V=1 \
    PACMAN="$pacman" \
    BUILDDIR="$BUILDDIR" \
    CARCH="$os_arch" \
    PKGEXT=".cgz" \
    CGZ_DESTDIR="$CGZ_DESTDIR" \
    PACKAGER="${PACKAGER:-Compass-CI}" \
    $LKP_SRC/sbin/makepkg "${makepkg_args[@]}"
    exit_code=$?

    if [[ $exit_code -eq 139 ]]; then
        die "Package build failed with SEGFAULT (exit code: $exit_code)"
    elif [[ $exit_code -ne 0 ]]; then
        die "Package build failed (exit code: $exit_code)"
    fi
}

create_latest_symlink() {
    local cgz_dir=${cgz_name%/*}
    local latest="$cgz_dir/latest.cgz"
    ln -sfv "${cgz_name##*/}" "$latest"
}

###############################################################################
# Main Execution
###############################################################################

# Track build start time (export for sub-shells)
export BUILD_START_TIME=$(date +%s)
echo "build_stage: 0"  # Build initialization started

check_vars
set_vars

# Setup build environment
echo "build_stage: 1"  # Environment setup
mkdir -p $TMP/$project
cd "$(mktemp -p $TMP/$project -d)" || die "Failed to create temp directory"
export CONFIG_DESTDIR=$PWD/config
export PATCHSET_DESTDIR=$PWD/patchset
export CGZ_DESTDIR=$PWD/cgz
export BUILDDIR=$PWD/build
mkdir -p "$CGZ_DESTDIR"

set_upstream_vars

echo "build_stage: 2"  # Getting package build files
# Get package build files
if [ -e "$LKP_SRC/programs/$project/PKGBUILD" ]; then
    cp "$LKP_SRC/programs/$project/PKGBUILD" .
elif [ -n "$pkgbuild_repo" ]; then
    download_pkgbuild
else
    set_pkgbuild_repo
    download_pkgbuild
fi

echo "build_stage: 3"  # Configuration and patches
# Handle configuration
set_remote_paths
get_config
get_patchset
prepare_pkgbuild
replace_pkgbuild_source

echo "build_stage: 4"  # Starting actual build
# Build and upload
build_package
BUILD_END_TIME=$(date +%s)
BUILD_DURATION=$((BUILD_END_TIME - BUILD_START_TIME))
echo "build_stage: 5"  # Build completed
echo "build_time: $BUILD_DURATION"  # Total build time in seconds

upload_results
upload_config

exit 0
# vim:set ts=4 sw=4 et:
